#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <exception>

#define SQLITE_HAS_CODEC 1
#include "sqlite3.h"

using namespace std;

enum BINDMAP_TYPE{
    // SQLITE_BIND_invalid = 0,
    SQLITE_BIND_int,     // INTEGER
    SQLITE_BIND_double,  // REAL
    SQLITE_BIND_string,  // TEXT
    SQLITE_BIND_void,    // NULL
    SQLITE_BIND_blob     // BLOB
};

typedef void* (*AutoBind_CallBack)(  // return: 当前处理参数的指针
    void* params,                    // IN: 传入当前整行的绑定数据结构体的指针
    int index,                       // IN: 处理参数的序号，从0开始（不同于绑定函数的index=1）
    BINDMAP_TYPE* type               // OUT: 返回当前处理参数的类型
);

/*-----------------------------------------------------------------*\
    functions of Sqlite3DB class
\*-----------------------------------------------------------------*/

class Sqlite3DB{
private:
    sqlite3 *_pHandle;
    sqlite3_stmt *_pStmt;
    char *_pErrMsg;  // 仅用于存储 sqlite3_exec() 和 sqlite3_get_table() 的错误信息
    struct SelInfo{
        int row;
        int column;
        char** value;  // 注意 _pSelect 是一个一维数组，包含数据表头信息
    } _selInfo;  // 仅用于存储 sqlite3_get_table() 查询结果

    string _curTable;

public:
    Sqlite3DB();
    ~Sqlite3DB();

private:
    Sqlite3DB(const Sqlite3DB& file_name){ }  // 限制赋值构造函数的使用

public:
    // 查询用回调函数
    static int callback_show_table(void *NotUsed, int argc, char **argv, char **azColName);

public:
    // 打开指定的数据库文件
    bool open(const string& file_name);
    bool close();
    bool assign_table(const string& table_name);
    void error();  // 打印Sqlite3错误消息
    void errmsg_free();  // 释放 sqlite3_exec() 错误消息
    void select_free();  // 释放 sqlite3_get_table() 的结果
    // Sqlite3执行函数
    bool exec(const string& strSQL, sqlite3_callback pFunc, void *argv=nullptr);
    bool prepare(const string& strSQL);
    bool bind(AutoBind_CallBack callback, void* params, int index = 0);
    bool step();
    bool show(const string& where = "", string table_name = "");  // 条件显示
    int select(const string& where, string table_name = "");  // 返回row：查找到的条目数量
    char* select(const string& where, int row, int column, string table_name = "");
    char* get_sel_value(int row = 0, int column = 0);
    // Sqlite3数据库加密
    bool lock(const string& passwd);  // return : true >> 加密成功；false >> 数据库已加密；Encrypt_Err >> 加密失败
    bool unlock(const string& passwd);
    bool setkey(const string& passwd, const string& new_passwd);
    bool clearkey(const string& passwd);
    // Sqlite3数据加密
};


/*-----------------------------------------------------------------*\
    functions of Exception class
\*-----------------------------------------------------------------*/

class Sqlite3Except: public exception{
public:
    Sqlite3Except() = default;
    Sqlite3Except(const char* const& description){
        _description = (char*)description;
    }
    virtual const char* what() const throw(){
        if(_description != nullptr)
            return _description;
        else
            return "SQLite3 Exception";
    }
protected:
    char* _description;
};

class Sqlite3Except_Select: public Sqlite3Except{
public:
    Sqlite3Except_Select(const char* const& description): Sqlite3Except(description){}
    virtual const char* what() const throw(){
        if(_description != nullptr)
            return _description;
        else
            return "SQLite3 Select Exception";
    }
};

class Sqlite3Except_Key: public Sqlite3Except{
public:
    Sqlite3Except_Key(const char* const& description): Sqlite3Except(description){}
    virtual const char* what() const throw(){
        if(_description != nullptr)
            return _description;
        else
            return "SQLite3 Key Exception";
    }
};


/*-----------------------------------------------------------------*\
    debug macro define
\*-----------------------------------------------------------------*/

#define __RT__DEBUG true
#include <string.h>

#define _print(note, ...) \
{	if(__RT__DEBUG) \
    printf("-- DEBUG -->> " note "\n",  ##__VA_ARGS__); }

#define _raise(note) \
{   char* morefunc; \
    asprintf(&morefunc, "-- EXCEPT -->> %s :: %s", __FUNCTION__, note); \
    throw morefunc; \
    free(morefunc); }

#define dim(x) (sizeof(x)/sizeof(x[0]))


// sqlite3 marco define

#define SQLite3DB_Batch(pDB, strSQL, list, func, node_num) \
    pDB->prepare(strSQL); \
    for(int i=0; i<(int)dim(list); i++){ \
        for(int j=0; j<node_num; j++) \
            pDB->bind(func, (void*)&list[i], j); \
        pDB->step(); }

#define SQLite_Bind_Node1(node_name, type0) \
struct node_name{ \
    type0 m_0; }; \
BINDMAP_TYPE list_type1[] = { \
    SQLITE_BIND_##type0 }

#define SQLite_Call_Back1(func_name, node_name) \
void* func_name(void* params, int index, BINDMAP_TYPE* type){ \
    node_name *node = (node_name*)params; \
    if(0 == index){ \
        *type = list_type1[index]; \
        return (void*)(&node->m_0); \
    }else{ \
        return nullptr; }}

#define SQLite_Bind_CallBack1(func, node, t0) \
    SQLite_Bind_Node1(node, t0); \
    SQLite_Call_Back1(func, node)


#define SQLite_Bind_Node2(node_name, type0, type1) \
struct node_name{ \
    type0 m_0; \
    type1 m_1; }; \
BINDMAP_TYPE list_type2[] = { \
    SQLITE_BIND_##type0, \
    SQLITE_BIND_##type1 }

#define SQLite_Call_Back2(func_name, node_name) \
void* func_name(void* params, int index, BINDMAP_TYPE* type){ \
    node_name *node = (node_name*)params; \
    if(0 == index){ \
        *type = list_type2[index]; \
        return (void*)(&node->m_0); \
    }else if(1 == index){ \
        *type = list_type2[index]; \
        return (void*)(&node->m_1); \
    }else{ \
        return nullptr; }}

#define SQLite_Bind_CallBack2(func, node, t0, t1) \
    SQLite_Bind_Node2(node, t0, t1); \
    SQLite_Call_Back2(func, node)


#define SQLite_Bind_Node3(node_name, type0, type1, type2) \
struct node_name{ \
    type0 m_0; \
    type1 m_1; \
    type2 m_2; }; \
BINDMAP_TYPE list_type3[] = { \
    SQLITE_BIND_##type0, \
    SQLITE_BIND_##type1, \
    SQLITE_BIND_##type2 }

#define SQLite_Call_Back3(func_name, node_name) \
void* func_name(void* params, int index, BINDMAP_TYPE* type){ \
    node_name *node = (node_name*)params; \
    if(0 == index){ \
        *type = list_type3[index]; \
        return (void*)(&node->m_0); \
    }else if(1 == index){ \
        *type = list_type3[index]; \
        return (void*)(&node->m_1); \
    }else if(2 == index){ \
        *type = list_type3[index]; \
        return (void*)(&node->m_2); \
    }else{ \
        return nullptr; }}

#define SQLite_Bind_CallBack3(func, node, t0, t1, t2) \
    SQLite_Bind_Node3(node, t0, t1, t2); \
    SQLite_Call_Back3(func, node)
